/*
MAXCLIENT-ben definiált maximális számú klienssel összeköttetés létesítése
A klienseket láncolt listában tárolja
Kilépő klienseket helyesen lekezeli, a láncolt listából törli
Ha egy klienstől “SET” parancs után egy sztringet kap, az azt jelenti, hogy a sztringben lévő karakterek azok,
melyekben érdekelt lesz az adott kliens. Innen kezdve ha bárki üzenetet küld a szervernek,
ez a kliens ebből az üzenetből az őt érdeklődő betűkről kap statisztikát: mely betűből hány darab volt az üzenetben.
Amíg a kliens nem küld SET parancsot, addig nem kap üzenetet, küldeni viszont küldhet.
“SET” üzenetről nem készül statisztika.

+1 jegy:
“?” hatására küldje el a szerver a legutóbbi 5 nem “SET” üzenetet,
továbbá az üzenet forrás IP-jét és portját az érdeklődő kliensnek. A “?” üzenetről se készüljön statisztika.

+ 1 jegy:
A betűkről készített statisztika mellett a szerver által elküldött üzenetben szerepeljen a kapott szavak száma is!
Vigyázz: a „Ez    egy próba      mondat” csak 4 szó!

+ 1 jegy:
A szerver csak a parancssori argumentumban megadott hosszúságú üzeneteket fogadja el
(és csak azokból készítsen statisztikát). Hosszabb beérkező üzenetre küldjön vissza „Túl hosszú üzenet” választ.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>

#define MAXCLIENT 2
#define LISTENPORT "2222"

struct kliens{
	int cfd;
	struct kliens* next;
	char betuk[200];
	int SET;
};
struct kliens* kliensek=NULL;
int kliensszam;

int listenSocket;
fd_set master;
int fdmax;

struct msg{
	char uz[200];
	struct msg* next;
};
struct msg* msgs= NULL;

void startlisten(){

	struct addrinfo krit;
	struct addrinfo* result;
	memset(&krit,0,sizeof krit);
	krit.ai_family=AF_INET;
	krit.ai_socktype=SOCK_STREAM;
	krit.ai_flags=AI_PASSIVE;
	getaddrinfo(NULL,LISTENPORT,&krit,&result);
	listenSocket=socket(result->ai_family,result->ai_socktype,result->ai_protocol);
	int v=1;
	setsockopt(listenSocket,SOL_SOCKET,SO_REUSEADDR,&v,sizeof v);
	if(bind(listenSocket,result->ai_addr,result->ai_addrlen))
		printf("bind error\n");
	listen(listenSocket,MAXCLIENT);

	FD_ZERO(&master);
	FD_SET(listenSocket,&master);
	fdmax=listenSocket;

}

void klienshozzaad(int ujfd,struct sockaddr sa){
	struct kliens* kl=(struct kliens*)malloc(sizeof(struct kliens));
	kl->cfd=ujfd;
	kl->next=kliensek;
	kl->SET=0;
	memset(kl->betuk, '\0', 200);
	kliensek=kl;
	kliensszam++;
	if (ujfd>fdmax) {
		fdmax=ujfd;
	}
	FD_SET(ujfd,&master);
}

void klienstorol(int i){
	struct kliens* torlendo;
	if (kliensek->cfd==i) {
		torlendo=kliensek;
		kliensek=kliensek->next;
	} else {
		torlendo=kliensek->next;
		struct kliens* elozo=kliensek;
		while(torlendo->cfd!=i){
			elozo=torlendo;
			torlendo=torlendo->next;
		}
		elozo->next=torlendo->next;
	}
	FD_CLR(torlendo->cfd,&master);
	int j;
	int ujfdmax;
	for (j = 0; j <= fdmax; ++j) {
		if (FD_ISSET(j,&master)) {
			ujfdmax=j;
		}
	}
	fdmax=ujfdmax;
	close(torlendo->cfd);
	free(torlendo);
	kliensszam--;
}

void lastmessages(int i){
	struct kliens* kl=kliensek;
	while(kl->cfd!=i){
		kl=kl->next;
	}
	char* uz[200];
	memset(uz,'\0',200);
	strcat(uz,"utolso 5 uzenet\n");
	send(kl->cfd,uz,sizeof uz,0);
	int j=0;
	struct msg* m=msgs;
	while(m!=NULL && j!=5){
		char* uz[200];
		memset(uz,'\0',200);
		strcat(uz,m->uz);
		send(kl->cfd,uz,sizeof uz,0);
		m=m->next;
		j++;
	}
}

void statkuldes(char s[200]){
	struct msg* m=(struct msg*)malloc(sizeof(struct msg));
	strcpy(m->uz,s);
	m->next=msgs;
	msgs=m;

	struct kliens* kl=kliensek;
	while(kl!=NULL){
		int b[200];
		int i;
		for (i = 0; i < strlen(s); ++i) {
			int j;
			for (j = 0; j < strlen(kl->betuk); ++j) {
				if (s[i]==kl->betuk[j]) {
					b[j]++;
				}
			}
		}
		int j;
		for (j = 0; j < strlen(kl->betuk); ++j) {
			if (b[j]>0) {
				char* uz[200];
				memset(uz,'\0',200);
				char st[2];
				st[0]=(char)kl->betuk[j];
				st[1]='\0';
				strcat(uz,st);
				strcat(uz," : ");
				char* n[10];
				sprintf(n,"%d",b[j]);
				strcat(uz,n);
				strcat(uz,"\n");
				send(kl->cfd,uz,sizeof uz,0);
			}
		}

		kl=kl->next;
	}
}

void kliensinput(int i){
	char inputbuffer[200];
	memset(inputbuffer,'\0',200);
	struct kliens* kl=kliensek;
	while(kl->cfd!=i){
		kl=kl->next;
	}
	int fogadottbytokszama=recv(i,inputbuffer,sizeof inputbuffer,0);
	if (fogadottbytokszama<=0) {
		klienstorol(i);
	} else {
		if (strncmp(inputbuffer,"\r\n",2)==0) {
			return;
		}
		if (strncmp(inputbuffer,"q",1)==0) {
			klienstorol(i);
			return;
		}
		if (strncmp(inputbuffer,"?",1)==0) {
			lastmessages(i);
			return;
		}
		if (strncmp(inputbuffer,"S ",2)==0) {
			int j;
			for (j = 2; j < fogadottbytokszama-2; ++j) {

				if (strchr( kl->betuk, inputbuffer[j])==NULL) {
					char st[2];
					st[0]=(char)inputbuffer[j];
					st[1]='\0';
					strcat(kl->betuk,st);
					printf("%c",inputbuffer[j]);
					kl->SET=1;
				}
			}
			printf("\n");
			fflush(stdout);
			return;
		}
		statkuldes(inputbuffer);
	}
}

void processloop(){
	fd_set readfd;
	FD_ZERO(&readfd);


	while(1){
		readfd=master;
		select(fdmax+1,&readfd,NULL,NULL,NULL);
		int i;
		for (i = 0; i <= fdmax; ++i) {
			if (FD_ISSET(i,&readfd)) {
				if (i==listenSocket) {
					struct sockaddr sa;
					socklen_t sl=sizeof sa;
					int ujfd=accept(listenSocket,&sa,&sl);
					if (kliensszam<MAXCLIENT) {
						klienshozzaad(ujfd,sa);
					} else {
						char* uz="limit\n";
						send(ujfd,uz,sizeof uz,0);
						close(ujfd);
					}

				} else {
					kliensinput(i);
				}
			}
		}

	}
}

int main(int argc, char **argv) {
	startlisten();
	processloop();
	return 0;
}

